﻿using ICodeShare.UI.Controls.Assists;
using ICodeShare.UI.Controls.Native;
using ICodeShare.UI.Controls.Utilities;
using ICodeShare.UI.Controls.Validations;
using Microsoft.Windows.Shell;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using System.Linq; 

namespace ICodeShare.UI.Controls
{
    [TemplatePart(Name = LayoutRootName, Type = typeof(Panel))]
    [TemplatePart(Name = ProgressBarName, Type = typeof(ProgressBar))]
    [TemplatePart(Name = CaptionName, Type = typeof(FrameworkElement))]
    [TemplatePart(Name = IconName, Type = typeof(Image))]
    [TemplatePart(Name = TitleName, Type = typeof(FrameworkElement))]
    [TemplatePart(Name = MinimizeName, Type = typeof(FrameworkElement))]
    [TemplatePart(Name = MaximizeName, Type = typeof(FrameworkElement))]
    [TemplatePart(Name = RestoreName, Type = typeof(FrameworkElement))]
    [TemplatePart(Name = CloseName, Type = typeof(FrameworkElement))]
    [TemplatePart(Name = GripName, Type = typeof(ResizeGrip))]
    public class BaseWindow : System.Windows.Window, IDisposable
    {
        #region Private consts

        private const string LayoutRootName = "PART_LayoutRoot";
        private const string ProgressBarName = "PART_ProgressBar";
        private const string CaptionName = "PART_Caption";
        private const string IconName = "PART_Icon";
        private const string TitleName = "PART_Title";
        private const string MinimizeName = "PART_Minimize";
        private const string MaximizeName = "PART_Maximize";
        private const string RestoreName = "PART_Restore";
        private const string CloseName = "PART_Close";
        private const string GripName = "PART_Grip";

        #endregion

        #region Private fields

        private FrameworkElement _caption;
        private Panel _layoutRoot;

        private IntPtr _handle;
        private Native.Window _window;

        [SecurityCritical]
        private WindowChrome _chrome;

        private bool _disposed;

        #endregion

        #region Constructors
        static BaseWindow()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(BaseWindow), new FrameworkPropertyMetadata(typeof(BaseWindow)));
            WindowStateProperty.OverrideMetadata(typeof(BaseWindow), new FrameworkPropertyMetadata(OnWindowStateChanged));
        }

        [SecuritySafeCritical]
        public BaseWindow()
        {
            Initialize();

            CommandBindings.Add(new CommandBinding(WindowCommands.Minimize, (sender, e) => WindowState = WindowState.Minimized));
            CommandBindings.Add(new CommandBinding(WindowCommands.Maximize, (sender, e) => WindowState = WindowState.Maximized));
            CommandBindings.Add(new CommandBinding(WindowCommands.Restore, (sender, e) => WindowState = WindowState.Normal));
            CommandBindings.Add(new CommandBinding(WindowCommands.Close, (sender, e) => Close()));
        }

        #endregion

        #region Initializers

        [SecurityCritical]
        private void Initialize()
        {
            // NOTE: Lack of contracts: SystemParameters2.Current must ensure non-null value
            Contract.Assume(SystemParameters2.Current != null);

            _chrome = new WindowChrome
            {
                CaptionHeight = SystemParameters2.Current.WindowCaptionHeight,
                CornerRadius = new CornerRadius(0d),
                GlassFrameThickness = new Thickness(0d),
                NonClientFrameEdges = NonClientFrameEdges.None,
                ResizeBorderThickness = WindowAssist.GetResizeBorderThickness(this),
                UseAeroCaptionButtons = false
            };
            _chrome.TryFreeze();
            if (WindowChrome.GetWindowChrome(this) == null)
            {
                WindowChrome.SetWindowChrome(this, _chrome);
            }

            Initialized += OnInitializedInternal;
            Loaded += OnLoadedInternal;

            var resizeBorderThicknessPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(WindowAssist.ResizeBorderThicknessProperty, typeof(BaseWindow));
            if (resizeBorderThicknessPropertyDescriptor != null)
            {
                resizeBorderThicknessPropertyDescriptor.AddValueChanged(this, OnResizeBorderThicknessChanged);
            }
        }

        private SizeToContent _previousSizeToContent = SizeToContent.Manual;

        [SecurityCritical]
        private void OnInitializedInternal(object sender, EventArgs e)
        {
            if (Equals(WindowChrome.GetWindowChrome(this), _chrome) && SizeToContent != SizeToContent.Manual)
            {
                _previousSizeToContent = SizeToContent;
                SizeToContent = SizeToContent.Manual;
            }
        }

        [SecurityCritical]
        private void OnLoadedInternal(object sender, RoutedEventArgs e)
        {
            if (Equals(WindowChrome.GetWindowChrome(this), _chrome) && _previousSizeToContent != SizeToContent.Manual)
            {
                SizeToContent = _previousSizeToContent;
                _previousSizeToContent = SizeToContent.Manual;

                if (WindowStartupLocation == WindowStartupLocation.CenterScreen)
                {
                    Left = SystemParameters.VirtualScreenLeft + SystemParameters.PrimaryScreenWidth / 2 - ActualWidth / 2;
                    Top = SystemParameters.VirtualScreenTop + SystemParameters.PrimaryScreenHeight / 2 - ActualHeight / 2;
                }
                if (WindowStartupLocation == WindowStartupLocation.CenterOwner)
                {
                    if (Owner != null)
                    {
                        if (Owner.WindowState == WindowState.Maximized)
                        {
                            var source = PresentationSource.FromVisual(Owner);
                            if (source != null && source.CompositionTarget != null)
                            {
                                var ownerHandle = new WindowInteropHelper(Owner).EnsureHandle();
                                var ownerWindow = new Native.Window(ownerHandle);
                                ownerWindow.Invalidate();
                                Left = -ownerWindow.NonClientBorderWidth * source.CompositionTarget.TransformFromDevice.M11;
                                Top = -ownerWindow.NonClientBorderHeight * source.CompositionTarget.TransformFromDevice.M22;
                            }
                            else
                            {
                                Left = 0;
                                Top = 0;
                            }
                        }
                        else
                        {
                            Left = Owner.Left;
                            Top = Owner.Top;
                        }
                        Left += Owner.ActualWidth / 2 - ActualWidth / 2;
                        Top += Owner.ActualHeight / 2 - ActualHeight / 2;
                    }
                }

                UpdateNonClientBorder();

                if (_dispatcherFrame != null)
                {
                    _dispatcherFrame.Continue = false;
                }
            }
        }

        [SecuritySafeCritical]
        protected override void OnSourceInitialized(EventArgs e)
        {
            Hook();
            base.OnSourceInitialized(e);
        }

        #endregion

        #region Interop

        [SecurityCritical]
        private void Hook()
        {
            _handle = new WindowInteropHelper(this).EnsureHandle();
            _window = new Native.Window(_handle);
            UpdateNonClientBorder();

            var source = HwndSource.FromHwnd(_handle);
            if (source != null)
            {
                source.AddHook(WndProc);
            }
        }

        [SecurityCritical]
        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            switch (msg)
            {
                case Interop.WM_GETMINMAXINFO:
                    if (Equals(WindowChrome.GetWindowChrome(this), _chrome))
                    {
                        GetMinMaxInfo(lParam);
                        handled = true;
                    }
                    break;
            }

            return (IntPtr)0;
        }

        [SecurityCritical]
        private void GetMinMaxInfo(IntPtr lParam)
        {
            var info = (Interop.MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(Interop.MINMAXINFO));

            var monitor = new Monitor(_handle);
            monitor.Invalidate();
            Taskbar.Invalidate();

            var bounds = monitor.Bounds;
            var workArea = monitor.WorkArea;
            info.ptMaxPosition.x = Math.Abs(bounds.left) + Taskbar.Position == TaskbarPosition.Left && Taskbar.AutoHide ? 1 : 0;
            info.ptMaxPosition.y = Math.Abs(bounds.top) + Taskbar.Position == TaskbarPosition.Top && Taskbar.AutoHide ? 1 : 0;
            info.ptMaxSize.x = info.ptMaxTrackSize.x = Math.Abs(workArea.right - workArea.left) - (Taskbar.Position == TaskbarPosition.Right && Taskbar.AutoHide ? 1 : 0);
            info.ptMaxSize.y = info.ptMaxTrackSize.y = Math.Abs(workArea.bottom - workArea.top) - (Taskbar.Position == TaskbarPosition.Bottom && Taskbar.AutoHide ? 1 : 0);

            var source = PresentationSource.FromVisual(this);
            if (source != null && source.CompositionTarget != null)
            {
                if (DoubleUtil.IsNonNegative(MinWidth))
                {
                    info.ptMinTrackSize.x = (int)Math.Ceiling(MinWidth * source.CompositionTarget.TransformFromDevice.M11);
                }
                if (DoubleUtil.IsNonNegative(MinHeight))
                {
                    info.ptMinTrackSize.y = (int)Math.Ceiling(MinHeight * source.CompositionTarget.TransformFromDevice.M22);
                }
                if (DoubleUtil.IsNonNegative(MaxWidth))
                {
                    info.ptMaxSize.x = info.ptMaxTrackSize.x = Math.Min(info.ptMaxSize.x, (int)Math.Ceiling(MaxWidth * source.CompositionTarget.TransformFromDevice.M11));
                }
                if (DoubleUtil.IsNonNegative(MaxHeight))
                {
                    info.ptMaxSize.y = info.ptMaxTrackSize.y = Math.Min(info.ptMaxSize.y, (int)Math.Ceiling(MaxHeight * source.CompositionTarget.TransformFromDevice.M22));
                }
            }

            Marshal.StructureToPtr(info, lParam, true);
        }

        #endregion
        
        #region Dependency Properties

        #region IsMainWindow 是否主窗体

        [AttachedPropertyBrowsableForType(typeof(System.Windows.Window))]
        public static bool GetIsMainWindow(System.Windows.Window obj)
        {
            return (bool)obj.GetValue(IsMainWindowProperty);
        }


        public static void SetIsMainWindow(System.Windows.Window obj, bool value)
        {
            obj.SetValue(IsMainWindowProperty, value);
        }

        public static readonly DependencyProperty IsMainWindowProperty = DependencyProperty.RegisterAttached(
            "IsMainWindow",
            typeof(bool),
            typeof(BaseWindow),
            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, OnIsMainWindowChanged));

        private static void OnIsMainWindowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var instance = (System.Windows.Window)d;
            if (instance != null && (bool)e.NewValue == true)
            {
                Action setMainWindow =
                    () =>
                    {
                        foreach (var window in Application.Current.Windows.AsParallel().Cast<System.Windows.Window>().Where(window => !Equals(window, instance)))
                        {
                            SetIsMainWindow(window, false);
                        }
                    };
                Application.Current.Dispatcher.BeginInvoke(setMainWindow, DispatcherPriority.Render);
            }
        }

        #endregion

        #region TitleBarProgressValue 标题栏进度条值

        [Category("Appearance")]
        [Description("The current magnitude of the progress bar that placed in the top of window.")]
        public double TitleBarProgressValue
        {
            get { return BoxingHelper<double>.Unbox(GetValue(TitleBarProgressValueProperty)); }
            set { SetValue(TitleBarProgressValueProperty, value); }
        }

        public static readonly DependencyProperty TitleBarProgressValueProperty = DependencyProperty.Register(
            "TitleBarProgressValue",
            typeof(double),
            typeof(BaseWindow),
            new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.None, OnTitleBarProgressValueChanged));

        private static void OnTitleBarProgressValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var instance = (BaseWindow)obj;
            instance.OnTitleBarProgressValueChanged((double)e.OldValue, (double)e.NewValue);
        }


        // ReSharper disable VirtualMemberNeverOverriden.Global
        protected virtual void OnTitleBarProgressValueChanged(double oldProgress, double newProgress)
        // ReSharper restore VirtualMemberNeverOverriden.Global
        {
        }

        #endregion

        #region IsTitleBarProgressBusy 是否正在运行

        [Category("Appearance")]
        [Description("Indicates busy state of window.")]
        public bool IsTitleBarProgressBusy
        {
            get { return (bool)GetValue(IsTitleBarProgressProperty); }
            set { SetValue(IsTitleBarProgressProperty, value); }
        }

        public static readonly DependencyProperty IsTitleBarProgressProperty = DependencyProperty.Register(
            "IsTitleBarProgressBusy",
            typeof(bool),
            typeof(BaseWindow),
            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, OnIsTitleBarProgressBusyChanged));

        private static void OnIsTitleBarProgressBusyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var instance = (BaseWindow)obj;
            instance.OnIsTitleBarProgressBusyChanged((bool)e.OldValue, (bool)e.NewValue);
        }


        // ReSharper disable VirtualMemberNeverOverriden.Global
        protected virtual void OnIsTitleBarProgressBusyChanged(bool oldIsBusy, bool newIsBusy)
        // ReSharper restore VirtualMemberNeverOverriden.Global
        {
        }

        #endregion

        #region HasDropShadow 是否立体效果

        [Category("Appearance")]
        [Description("Indicates has window drop shadow or not.")]
        public bool HasDropShadow
        {
            get { return (bool)GetValue(HasDropShadowProperty); }
            set { SetValue(HasDropShadowProperty, value); }
        }

        public static readonly DependencyProperty HasDropShadowProperty = DependencyProperty.Register(
            "HasDropShadow",
            typeof(bool),
            typeof(BaseWindow),
            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, OnHasDropShadowChanged, CoerceHasDropShadow));

        private static void OnHasDropShadowChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var instance = (BaseWindow)obj;
            instance.OnHasDropShadowChanged((bool)e.OldValue, (bool)e.NewValue);
        }


        [SecuritySafeCritical]
        // ReSharper disable VirtualMemberNeverOverriden.Global
        protected virtual void OnHasDropShadowChanged(bool oldHasDropShadow, bool newHasDropShadow)
        // ReSharper restore VirtualMemberNeverOverriden.Global
        {
            OnHasDropShadowChangedInternal(newHasDropShadow);
        }

        [SecurityCritical]
        private void OnHasDropShadowChangedInternal(bool newHasDropShadow)
        {
            if (Equals(WindowChrome.GetWindowChrome(this), _chrome))
            {
                _chrome = new WindowChrome
                {
                    CaptionHeight = _chrome.CaptionHeight,
                    CornerRadius = _chrome.CornerRadius,
                    GlassFrameThickness = !newHasDropShadow ? new Thickness(0d) : new Thickness(0d, 0d, 0d, 1d),
                    NonClientFrameEdges = _chrome.NonClientFrameEdges,
                    ResizeBorderThickness = _chrome.ResizeBorderThickness,
                    UseAeroCaptionButtons = _chrome.UseAeroCaptionButtons
                };
                _chrome.TryFreeze();
                WindowChrome.SetWindowChrome(this, _chrome);
            }
        }

        private static object CoerceHasDropShadow(DependencyObject obj, object basevalue)
        {
            ValidationHelper.NotNull(obj, "obj");
            try
            {
                // NOTE: Ignore Code Contracts warnings
                return (bool)basevalue && SystemParameters.DropShadow && Windows.IsWindowsVistaOrHigher;
            }
            catch
            {
                return basevalue;
            }
        }

        #endregion

        #region ApplicationBar 应用程序菜单

        [AttachedPropertyBrowsableForType(typeof(System.Windows.Window))]
        public static ApplicationBar GetApplicationBar(System.Windows.Window obj)
        {
            return (ApplicationBar)obj.GetValue(ApplicationBarProperty);
        }

        /// <summary>
        /// Set where show the shadow for waiting data
        /// </summary>
        public static void SetApplicationBar(DependencyObject element, ApplicationBar value)
        {
            element.SetValue(ApplicationBarProperty, value);
        }

        public static readonly DependencyProperty ApplicationBarProperty = DependencyProperty.RegisterAttached(
            "ApplicationBar",
            typeof(ApplicationBar),
            typeof(BaseWindow),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, OnApplicationBarChanged));

        private static void OnApplicationBarChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            ValidationHelper.NotNull(obj, "obj");
            var instance = obj as System.Windows.Window;
            if (instance != null && e.OldValue != null)
            {
                instance.MouseRightButtonUp -= OnApplicationBarVisibilityChanged;
            }
            if (instance != null && e.NewValue != null)
            {
                instance.MouseRightButtonUp += OnApplicationBarVisibilityChanged;
            }
        }

        private static void OnApplicationBarVisibilityChanged(object sender, MouseButtonEventArgs e)
        {
            var window = sender as System.Windows.Window;
            var source = e.OriginalSource as UIElement;
            if (window != null && source != null && !ApplicationBar.GetPreventsOpen(source))
            {
                var applicationBar = GetApplicationBar(window);
                if (applicationBar != null)
                {
                    if (applicationBar.IsOpening || applicationBar.IsClosing)
                    {
                        if (applicationBar.IsOpening)
                        {
                            applicationBar.Opened += ChangeVisibilityAfterOpened;
                        }
                        // ReSharper disable ConditionIsAlwaysTrueOrFalse
                        else if (applicationBar.IsClosing)
                        // ReSharper restore ConditionIsAlwaysTrueOrFalse
                        {
                            applicationBar.Closed += ChangeVisibilityAfterClosed;
                        }
                    }
                    else
                    {
                        applicationBar.IsOpen = !applicationBar.StaysOpen || !applicationBar.IsOpen;
                    }
                }
            }
        }

        private static void ChangeVisibilityAfterOpened(object sender, EventArgs e)
        {
            var applicationBar = sender as ApplicationBar;
            if (applicationBar != null)
            {
                applicationBar.IsOpen = !applicationBar.StaysOpen || !applicationBar.IsOpen;
                applicationBar.Opened -= ChangeVisibilityAfterOpened;
            }
        }

        private static void ChangeVisibilityAfterClosed(object sender, EventArgs e)
        {
            var applicationBar = sender as ApplicationBar;
            if (applicationBar != null)
            {
                applicationBar.IsOpen = !applicationBar.StaysOpen || !applicationBar.IsOpen;
                applicationBar.Closed -= ChangeVisibilityAfterClosed;
            }
        }
        #endregion

        #region IsApplicationBarHost 是否主菜单容器

        [AttachedPropertyBrowsableForType(typeof(Decorator))]
        public static bool GetIsApplicationBarHost(Decorator obj)
        {
            return (bool)obj.GetValue(IsApplicationBarHostProperty);
        }


        public static void SetIsApplicationBarHost(Decorator obj, bool value)
        {
            obj.SetValue(IsApplicationBarHostProperty, value);
        }

        public static readonly DependencyProperty IsApplicationBarHostProperty = DependencyProperty.RegisterAttached(
            "IsApplicationBarHost",
            typeof(bool),
            typeof(BaseWindow),
            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, OnIsApplicationBarHostChanged));




        private static void OnIsApplicationBarHostChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var host = obj as Decorator;
            if (host != null)
            {
                var window = TreeHelper.FindParent<System.Windows.Window>(host);
                if (window != null)
                {
                    var applicationBar = GetApplicationBar(window);
                    host.Child = (bool)e.NewValue ? applicationBar : null;
                }
            }
        }

        #endregion

        #region TitleBar

        #region TitleBar 标题栏

        [AttachedPropertyBrowsableForType(typeof(BaseWindow))]
        public static FrameworkElement GetTitleBar(BaseWindow obj)
        {
            ValidationHelper.NotNull(obj, "obj");
            return (FrameworkElement)obj.GetValue(TitleBarProperty);
        }


        public static void SetTitleBar(BaseWindow obj, FrameworkElement value)
        {
            ValidationHelper.NotNull(obj, "obj");
            obj.SetValue(TitleBarProperty, value);
        }

        public static readonly DependencyProperty TitleBarProperty = DependencyProperty.RegisterAttached(
            "TitleBar",
            typeof(FrameworkElement),
            typeof(BaseWindow),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));


        #endregion

        #region IsTitleBarHost 是否是标题栏容器

        [AttachedPropertyBrowsableForType(typeof(Decorator))]
        public static bool GetIsTitleBarHost(Decorator obj)
        {
            return (bool)obj.GetValue(IsTitleBarHostProperty);
        }


        public static void SetIsTitleBarHost(Decorator obj, bool value)
        {
            obj.SetValue(IsTitleBarHostProperty, value);
        }

        public static readonly DependencyProperty IsTitleBarHostProperty = DependencyProperty.RegisterAttached(
            "IsTitleBarHost",
            typeof(bool),
            typeof(BaseWindow),
            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, OnIsTitleBarHostChanged));

        private static void OnIsTitleBarHostChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var host = obj as Decorator;
            if (host != null)
            {
                var window = TreeHelper.FindParent<BaseWindow>(host);
                if (window != null)
                {
                    var titleBar = GetTitleBar(window);
                    host.Child = (bool)e.NewValue ? titleBar : null;
                }
            }
        }

        #endregion

        #region TitleBarBackground

        /// <summary>
        /// Gets or sets the title bar background.
        /// </summary>
        /// <value>
        /// The title bar background.
        /// </value>
        public Brush TitleBarBackground
        {
            get { return (Brush)this.GetValue(TitleBarBackgroundProperty); }
            set { this.SetValue(TitleBarBackgroundProperty, value); }
        }

        public static readonly DependencyProperty TitleBarBackgroundProperty = DependencyProperty.Register(
            "TitleBarBackground",
            typeof(Brush),
            typeof(BaseWindow),
            new PropertyMetadata(null));

        #endregion

        #region TitleBarBorderBrush

        /// <summary>
        /// Gets or sets the title bar border brush.
        /// </summary>
        /// <value>
        /// The title bar border brush.
        /// </value>
        public Brush TitleBarBorderBrush
        {
            get { return (Brush)this.GetValue(TitleBarBorderBrushProperty); }
            set { this.SetValue(TitleBarBorderBrushProperty, value); }
        }

        public static readonly DependencyProperty TitleBarBorderBrushProperty = DependencyProperty.Register(
            "TitleBarBorderBrush",
            typeof(Brush),
            typeof(BaseWindow),
            new PropertyMetadata(null));

        #endregion

        #region TitleBarBorderThickness

        /// <summary>
        /// Gets or sets the title bar border thickness.
        /// </summary>
        /// <value>
        /// The title bar border thickness.
        /// </value>
        public Thickness TitleBarBorderThickness
        {
            get { return (Thickness)this.GetValue(TitleBarBorderThicknessProperty); }
            set { this.SetValue(TitleBarBorderThicknessProperty, value); }
        }


        public static readonly DependencyProperty TitleBarBorderThicknessProperty = DependencyProperty.Register(
            "TitleBarBorderThickness",
            typeof(Thickness),
            typeof(BaseWindow),
            new PropertyMetadata(new Thickness()));

        #endregion

        #region TitleBarContent

        /// <summary>
        /// Gets or sets the content of the title bar.
        /// </summary>
        /// <value>
        /// The content of the title bar.
        /// </value>
        public object TitleBarContent
        {
            get { return (object)this.GetValue(TitleBarContentProperty); }
            set { this.SetValue(TitleBarContentProperty, value); }
        }

        public static readonly DependencyProperty TitleBarContentProperty = DependencyProperty.Register(
            "TitleBarContent",
            typeof(object),
            typeof(BaseWindow),
            new PropertyMetadata(null));

        #endregion

        #region TitleBarContentTemplate

        /// <summary>
        /// Gets or sets the content of the title bar.
        /// </summary>
        /// <value>
        /// The content of the title bar.
        /// </value>
        public DataTemplate TitleBarContentTemplate
        {
            get { return (DataTemplate)this.GetValue(TitleBarContentTemplateProperty); }
            set { this.SetValue(TitleBarContentTemplateProperty, value); }
        }

        public static readonly DependencyProperty TitleBarContentTemplateProperty = DependencyProperty.Register(
            "TitleBarContentTemplate",
            typeof(DataTemplate),
            typeof(BaseWindow),
            new PropertyMetadata(null));

        #endregion

        #region TitleBarContentTemplateSelector

        /// <summary>
        /// Gets or sets the title content template selector.
        /// </summary>
        /// <value>
        /// The title content template selector.
        /// </value>
        public DataTemplateSelector TitleBarContentTemplateSelector
        {
            get { return (DataTemplateSelector)this.GetValue(TitleBarContentTemplateSelectorProperty); }
            set { this.SetValue(TitleBarContentTemplateSelectorProperty, value); }
        }

        public static readonly DependencyProperty TitleBarContentTemplateSelectorProperty = DependencyProperty.Register(
            "TitleBarContentTemplateSelector",
            typeof(DataTemplateSelector),
            typeof(BaseWindow),
            new PropertyMetadata(null));

        #endregion

        #region TitleBarForeground

        /// <summary>
        /// Gets or sets the title bar foreground.
        /// </summary>
        /// <value>
        /// The title bar foreground.
        /// </value>
        public Brush TitleBarForeground
        {
            get { return (Brush)this.GetValue(TitleBarForegroundProperty); }
            set { this.SetValue(TitleBarForegroundProperty, value); }
        }

        public static readonly DependencyProperty TitleBarForegroundProperty = DependencyProperty.Register(
            "TitleBarForeground",
            typeof(Brush),
            typeof(BaseWindow),
            new PropertyMetadata(null));

        #endregion

        #region TitleBarVisibility

        /// <summary>
        /// Gets or sets the title bar visibility.
        /// </summary>
        /// <value>
        /// The title bar visibility.
        /// </value>
        public Visibility TitleBarVisibility
        {
            get { return (Visibility)this.GetValue(TitleBarVisibilityProperty); }
            set { this.SetValue(TitleBarVisibilityProperty, value); }
        }

        public static readonly DependencyProperty TitleBarVisibilityProperty = DependencyProperty.Register(
            "TitleBarVisibility",
            typeof(Visibility),
            typeof(BaseWindow),
            new PropertyMetadata(Visibility.Visible));

        #endregion

        #endregion

        
        #endregion

        #region Overridden methods


        [SecurityCritical]
        public new bool? ShowDialog()
        {
            if (Equals(WindowChrome.GetWindowChrome(this), _chrome) && SizeToContent != SizeToContent.Manual)
            {
                new UIPermission(PermissionState.Unrestricted).Demand();
                _dispatcherFrame = new DispatcherFrame();
                Show();
                Dispatcher.PushFrame(_dispatcherFrame);
                _dispatcherFrame = null;
                return base.ShowDialog();
            }
            return base.ShowDialog();
        }

        private DispatcherFrame _dispatcherFrame;

        [SecuritySafeCritical]
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            OnApplyTemplateInternal();
        }

        [SecurityCritical]
        private void OnApplyTemplateInternal()
        {
            if (Template != null)
            {
                _caption = Template.FindName(CaptionName, this) as FrameworkElement;
                if (_caption == null)
                {
                    Trace.TraceError(CaptionName + " not found.");
                }
                else
                {
                    _caption.SizeChanged += OnCaptionSizeChanged;
                }

                // NOTE: Lack of contracts: FindName must be marked as pure method
                Contract.Assume(Template != null);
                _layoutRoot = Template.FindName(LayoutRootName, this) as Panel;
                if (_layoutRoot == null)
                {
                    Trace.TraceError(LayoutRootName + " not found.");
                }
            }
        }

        [SecurityCritical]
        private void OnCaptionSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (e.HeightChanged && Equals(WindowChrome.GetWindowChrome(this), _chrome))
            {
                _chrome = new WindowChrome
                {
                    CaptionHeight = e.NewSize.Height,
                    CornerRadius = _chrome.CornerRadius,
                    GlassFrameThickness = _chrome.GlassFrameThickness,
                    NonClientFrameEdges = _chrome.NonClientFrameEdges,
                    ResizeBorderThickness = _chrome.ResizeBorderThickness,
                    UseAeroCaptionButtons = _chrome.UseAeroCaptionButtons
                };
                _chrome.TryFreeze();
                WindowChrome.SetWindowChrome(this, _chrome);
            }
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            Dispose();
        }

        #endregion

        #region Handlers

        [SecurityCritical]
        private void OnResizeBorderThicknessChanged(object sender, EventArgs e)
        {
            if (Equals(WindowChrome.GetWindowChrome(this), _chrome))
            {
                _chrome = new WindowChrome
                {
                    CaptionHeight = _chrome.CaptionHeight,
                    CornerRadius = _chrome.CornerRadius,
                    GlassFrameThickness = _chrome.GlassFrameThickness,
                    NonClientFrameEdges = _chrome.NonClientFrameEdges,
                    ResizeBorderThickness = WindowAssist.GetResizeBorderThickness(this),
                    UseAeroCaptionButtons = _chrome.UseAeroCaptionButtons
                };
                _chrome.TryFreeze();
                WindowChrome.SetWindowChrome(this, _chrome);
            }
        }

        [SecuritySafeCritical]
        private static void OnWindowStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            ValidationHelper.NotNull(obj, "obj");
            var instance = (BaseWindow)obj;
            instance.OnWindowStateChanged();
        }

        [SecurityCritical]
        private void OnWindowStateChanged()
        {
            UpdateNonClientBorder();
        }

        [SecurityCritical]
        private void UpdateNonClientBorder()
        {
            if (!Equals(WindowChrome.GetWindowChrome(this), _chrome) || _layoutRoot == null || _window == null)
            {
                return;
            }

            Taskbar.Invalidate();
            if (WindowState == WindowState.Maximized && !Taskbar.AutoHide && SizeToContent == SizeToContent.Manual)
            {
                _window.Invalidate();
                _layoutRoot.Margin = new Thickness(_window.NonClientBorderWidth,
                                                   _window.NonClientBorderHeight,
                                                   _window.NonClientBorderWidth,
                                                   _window.NonClientBorderHeight);
            }
            else
            {
                _layoutRoot.Margin = new Thickness();
            }
        }

        #endregion

        #region Global static properties

        public static ResourceKey DefaultCaptionButtonStyleKey
        {
            get
            {
                return _defaultCaptionButtonStyleKey ?? (_defaultCaptionButtonStyleKey = new ComponentResourceKey(typeof(BaseWindow), "DefaultCaptionButtonStyleKey"));
            }
        }

        private static ResourceKey _defaultCaptionButtonStyleKey;

        public static ResourceKey MainWindowCaptionButtonStyleKey
        {
            get { return _mainWindowCaptionButtonStyleKey ?? (_mainWindowCaptionButtonStyleKey = new ComponentResourceKey(typeof(BaseWindow), "MainWindowCaptionButtonStyleKey")); }
        }

        private static ResourceKey _mainWindowCaptionButtonStyleKey;

        #endregion

        #region Implementation of IDisposable

        [SecuritySafeCritical]
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        [SecurityCritical]
        // ReSharper disable VirtualMemberNeverOverriden.Global
        protected virtual void Dispose(bool disposing)
        // ReSharper restore VirtualMemberNeverOverriden.Global
        {
            if (_disposed)
            {
                return;
            }

            if (disposing)
            {
                _chrome = null;

                var resizeBorderThicknessPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(WindowAssist.ResizeBorderThicknessProperty, typeof(BaseWindow));
                if (resizeBorderThicknessPropertyDescriptor != null)
                {
                    resizeBorderThicknessPropertyDescriptor.RemoveValueChanged(this, OnResizeBorderThicknessChanged);
                }
            }

            _disposed = true;
        }

        [SecuritySafeCritical]
        ~BaseWindow()
        {
            Dispose(false);
        }

        #endregion
    }
}
